package com.qjc.demo.base.map;

import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.*;

/**
 * @Description: 比较安全的HashMap：Hashtable，Collections.synchronizedMap()，ConcurrentHashMap的执行效率
 * @Author: qjc
 * @Date: 2020/7/27
 */
public class SafeMapTest {

    /**
     * 可替代HashMap的线程安全的集合？
     * 1、ConcurrentHashMap
     * JDK1.7用的是Segment（分段锁），JDK1.8使用了Synchronized + CAS算法
     * 2、HashTable
     * put方法和get方法都用了synchronized关键字修饰
     * 3、Collections.synchronizedMap()
     * 使用的也是syncronized关键字
     * 总结：ConcurrentHashMap的效率比HashTable和Collections.synchronizedMap()效率高了一半还多
     */

    public final static int THREAD_POOL_SIZE = 5;
    private final static long KEEP_ALIVE_TIME = 0L;

    public static Map<String, Integer> crunchifyHashTableObject = null;
    public static Map<String, Integer> crunchifySynchronizedMapObject = null;
    public static Map<String, Integer> crunchifyConcurrentHashMapObject = null;

    public static void main(String[] args) throws InterruptedException {

        // Test with Hashtable Object
        crunchifyHashTableObject = new Hashtable<>();
        crunchifyPerformTest(crunchifyHashTableObject);

        // Test with synchronizedMap Object
        crunchifySynchronizedMapObject = Collections.synchronizedMap(new HashMap<>());
        crunchifyPerformTest(crunchifySynchronizedMapObject);

        // Test with ConcurrentHashMap Object
        crunchifyConcurrentHashMapObject = new ConcurrentHashMap<>();
        crunchifyPerformTest(crunchifyConcurrentHashMapObject);

    }

    public static void crunchifyPerformTest(final Map<String, Integer> crunchifyThreads) throws InterruptedException {

        System.out.println("Test started for: " + crunchifyThreads.getClass());
        long averageTime = 0;
        for (int i = 0; i < 5; i++) {
            long startTime = System.nanoTime();
//            ExecutorService crunchifyExServer = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
            ExecutorService crunchifyExServer = new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, KEEP_ALIVE_TIME,
                    TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>(10), new ThreadPoolExecutor.AbortPolicy());
            for (int j = 0; j < THREAD_POOL_SIZE; j++) {
                crunchifyExServer.execute(() -> {
                    for (int k = 0; k < 500000; k++) {
                        Integer crunchifyRandomNumber = (int) Math.ceil(Math.random() * 550000);
                        // Retrieve value. We are not using it anywhere
                        Integer crunchifyValue = crunchifyThreads.get(String.valueOf(crunchifyRandomNumber));
                        // Put value
                        crunchifyThreads.put(String.valueOf(crunchifyRandomNumber), crunchifyRandomNumber);
                    }
                });
            }
            // Make sure executor stops
            crunchifyExServer.shutdown();
            // Blocks until all tasks have completed execution after a shutdown request
            crunchifyExServer.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);

            long entTime = System.nanoTime();
            long totalTime = (entTime - startTime) / 1000000L;
            averageTime += totalTime;
            System.out.println("250w entried added/retrieved in " + totalTime + " ms");
        }
        System.out.println("For " + crunchifyThreads.getClass() + " the average time is " + averageTime / 5 + " ms\n");
    }
}
